/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#include "sg_local.h"


typedef struct {
	int		numSnapshotEntities;
	int		snapshotEntities[MAX_SNAPSHOT_ENTITIES];
} snapshotEntityNumbers_t;


/*
 ==================
 SG_SortEntityNumbers
 ==================
*/
static int SG_SortEntityNumbers (const void *elem1, const void *elem2){

	const int	*entNum1 = (const int *)elem1;
	const int	*entNum2 = (const int *)elem2;

	if (*entNum1 == *entNum2)
		odSystem->Error(false, "SG_SortEntityNumbers: duplicated entity");

	if (*entNum1 < *entNum2)
		return -1;

	return 1;
}

/*
 ==================
 SG_AddEntitiesVisibleFromPoint
 ==================
*/
static void SG_AddEntitiesVisibleFromPoint (const odVec3 &point, snapshotEntityNumbers_t *entityNumbers){

	entity_t	*entity;
	int			leaf, cluster, area;
	int			i;

	leaf = odCollision->PointInLeaf(point);

	cluster = odCollision->LeafCluster(leaf);
	area = odCollision->LeafArea(leaf);

	// Check all the entities
	for (i = 0; i < MAX_ENTITIES; i++){
		entity = level.entities[i];
		if (!entity)
			continue;

		if (!entity->linked)
			continue;		// Not linked

		if (entity->snapshotCount == level.snapshotCount)
			continue;		// Already added this entity from another view
		entity->snapshotCount = level.snapshotCount;

		// Make sure the number is correct (FIXME: is this still happening?)
		if (entity->state.number != i){
			odSystem->DPrintf(S_COLOR_YELLOW "FIXING ENTITY->STATE.NUMBER!!!\n");

			entity->state.number = i;
		}

		// TODO: if it has a sound, but it's not in PVS, send it anyway (but
		// clear model, effects, etc). The sound can also be culled out (by
		// distance) to save bandwidth.

		// Ignore it if not in PVS
		if (!odCollision->BoundsInPVS(entity->absBounds, cluster, area))
			continue;

		// Add it
		if (entityNumbers->numSnapshotEntities == MAX_SNAPSHOT_ENTITIES)
			break;

		entityNumbers->snapshotEntities[entityNumbers->numSnapshotEntities++] = entity->state.number;

		// TODO: subviews (also set viewType_t on each entity for this client)

		// If this is a portal sky entity, add everything visible from its
		// target position
//		if (entity->flags & EF_PORTALSKY){
//			if (!odCollision->LeafCanSeePortalSky(leaf))
//				continue;		// Portal sky can't be seen from this point

//			SG_AddEntitiesVisibleFromPoint(entity->origin, entityNumbers);
//		}

		// If this is a portal or remote camera entity, add everything visible
		// from its target position
//		if (entity->flags & (EF_PORTAL | EF_REMOTE))
//			SG_AddEntitiesVisibleFromPoint(entity->origin, entityNumbers);
	}
}

/*
 ==================
 SG_BuildClientSnapshot

 Decides which entities are going to be visible to the client, and copies off
 the player state.

 This properly handles multiple recursive subviews, but the renderer currently
 doesn't.
 ==================
*/
static clientSnapshot_t *SG_BuildClientSnapshot (client_t *client){

	snapshotEntityNumbers_t	entityNumbers;
	clientSnapshot_t		*snapshot;
	entity_t				*entity;
	odVec3					point;
	int						i;

	// Bump snapshot count
	level.snapshotCount++;

	// Clear entity numbers
	entityNumbers.numSnapshotEntities = 0;

	// Never send client's own entity, because it can be regenerated from the
	// player state
	level.entities[client->persistent.clientNum]->snapshotCount = level.snapshotCount;

	// Find the client's view point
	point = client->state.origin;
	point.z += 75.0f;		// TODO!!!

	// Add all the entities directly visible to the eye, which may include
	// subviews that merge other entities
	SG_AddEntitiesVisibleFromPoint(point, &entityNumbers);

	// If there were subviews, there may be out of order entities in the list
	// which will need to be resorted for the delta compression to work
	// correctly. This also catches the error condition of an entity being
	// included twice.
	qsort(entityNumbers.snapshotEntities, entityNumbers.numSnapshotEntities, sizeof(int), SG_SortEntityNumbers);

	// This is the snapshot we are creating
	snapshot = (clientSnapshot_t *)odMemory->Alloc(sizeof(clientSnapshot_t), TAG_SERVER);

	snapshot->state = client->state;

	snapshot->numEntities = entityNumbers.numSnapshotEntities;
	snapshot->entities = (entityState_t *)odMemory->Alloc(snapshot->numEntities * sizeof(entityState_t), TAG_SERVER);

	// Copy the entity states out
	for (i = 0; i < entityNumbers.numSnapshotEntities; i++){
		entity = level.entities[entityNumbers.snapshotEntities[i]];

		snapshot->entities[i] = entity->state;
	}

	return snapshot;
}


// ============================================================================


/*
 ==================
 SG_WriteDeltaPlayerState

 TODO!!!
 ==================
*/
static void SG_WriteDeltaPlayerState (playerState_t *oldState, playerState_t *newState, ByteBuffer *msg){

	playerState_t	nullState;

	if (!oldState){
		oldState = &nullState;
		memset(oldState, 0, sizeof(playerState_t));
	}

	msg->WriteByte(newState->clientNum);

	msg->WriteDeltaFloat(oldState->origin.x, newState->origin.x);
	msg->WriteDeltaFloat(oldState->origin.y, newState->origin.y);
	msg->WriteDeltaFloat(oldState->origin.z, newState->origin.z + 75.0f);

	msg->WriteDeltaFloat(oldState->velocity.x, newState->velocity.x);
	msg->WriteDeltaFloat(oldState->velocity.y, newState->velocity.y);
	msg->WriteDeltaFloat(oldState->velocity.z, newState->velocity.z);

	msg->WriteDeltaAngle16(oldState->angles.pitch, newState->angles.pitch);
	msg->WriteDeltaAngle16(oldState->angles.yaw, newState->angles.yaw);
	msg->WriteDeltaAngle16(oldState->angles.roll, newState->angles.roll);

	msg->WriteDeltaShort(oldState->deltaAngles[0], newState->deltaAngles[0]);
	msg->WriteDeltaShort(oldState->deltaAngles[1], newState->deltaAngles[1]);
	msg->WriteDeltaShort(oldState->deltaAngles[2], newState->deltaAngles[2]);
}

/*
 ==================
 SG_WriteDeltaEntityState

 TODO!!!
 ==================
*/
static void SG_WriteDeltaEntityState (entityState_t *oldState, entityState_t *newState, bool force, ByteBuffer *msg){

	int		diff;

	// A NULL newState is a delta remove
	if (newState == NULL){
		msg->WriteBits(oldState->number, ENTITYNUM_BITS);
		msg->WriteBits(1, 1);
		return;
	}

	// Check for changes
	diff = memcmp(oldState, newState, sizeof(entityState_t));

	if (!diff){
		// Nothing at all changed
		if (!force)
			return;		// Nothing at all

		msg->WriteBits(newState->number, ENTITYNUM_BITS);
		msg->WriteBits(0, 1);		// Not removed
		msg->WriteBits(0, 1);		// No delta
		return;
	}

	msg->WriteBits(newState->number, ENTITYNUM_BITS);
	msg->WriteBits(0, 1);		// Not removed
	msg->WriteBits(1, 1);		// We have a delta

	// Delta compress the entity state
	msg->WriteByte(newState->type);

	if (newState->type == ET_LIGHT){
		msg->WriteDeltaFloat(oldState->origin.x, newState->origin.x);
		msg->WriteDeltaFloat(oldState->origin.y, newState->origin.y);
		msg->WriteDeltaFloat(oldState->origin.z, newState->origin.z);
		msg->WriteDeltaFloat(oldState->center.x, newState->center.x);
		msg->WriteDeltaFloat(oldState->center.y, newState->center.y);
		msg->WriteDeltaFloat(oldState->center.z, newState->center.z);
		msg->WriteDeltaAngle16(oldState->angles.pitch, newState->angles.pitch);
		msg->WriteDeltaAngle16(oldState->angles.yaw, newState->angles.yaw);
		msg->WriteDeltaAngle16(oldState->angles.roll, newState->angles.roll);
		msg->WriteDeltaFloat(oldState->radius.x, newState->radius.x);
		msg->WriteDeltaFloat(oldState->radius.y, newState->radius.y);
		msg->WriteDeltaFloat(oldState->radius.z, newState->radius.z);
		msg->WriteDeltaFloat(oldState->fovX, newState->fovX);
		msg->WriteDeltaFloat(oldState->fovY, newState->fovY);
		msg->WriteDeltaFloat(oldState->falloffRange, newState->falloffRange);
		msg->WriteBool(newState->projected);
		msg->WriteBool(newState->parallel);
		msg->WriteBool(newState->noShadows);
		msg->WriteDeltaLong(oldState->detailLevel, newState->detailLevel);
		msg->WriteDeltaLong(oldState->materialIndex, newState->materialIndex);
		msg->WriteDeltaFloat(oldState->materialParms[0], newState->materialParms[0]);
		msg->WriteDeltaFloat(oldState->materialParms[1], newState->materialParms[1]);
		msg->WriteDeltaFloat(oldState->materialParms[2], newState->materialParms[2]);
		msg->WriteDeltaFloat(oldState->materialParms[3], newState->materialParms[3]);
		msg->WriteDeltaFloat(oldState->materialParms[4], newState->materialParms[4]);
		msg->WriteDeltaFloat(oldState->materialParms[5], newState->materialParms[5]);
		msg->WriteDeltaFloat(oldState->materialParms[6], newState->materialParms[6]);
		msg->WriteDeltaFloat(oldState->materialParms[7], newState->materialParms[7]);
	}
	else {
		msg->WriteDeltaLong(oldState->flags, newState->flags);
		msg->WriteDeltaFloat(oldState->origin.x, newState->origin.x);
		msg->WriteDeltaFloat(oldState->origin.y, newState->origin.y);
		msg->WriteDeltaFloat(oldState->origin.z, newState->origin.z);
		msg->WriteDeltaAngle16(oldState->angles.pitch, newState->angles.pitch);
		msg->WriteDeltaAngle16(oldState->angles.yaw, newState->angles.yaw);
		msg->WriteDeltaAngle16(oldState->angles.roll, newState->angles.roll);
		msg->WriteDeltaLong(oldState->modelIndex, newState->modelIndex);
		msg->WriteDeltaLong(oldState->clipModelIndex, newState->clipModelIndex);
		msg->WriteDeltaLong(oldState->materialIndex, newState->materialIndex);
		msg->WriteDeltaLong(oldState->soundShaderIndex, newState->soundShaderIndex);
		msg->WriteDeltaFloat(oldState->materialParms[0], newState->materialParms[0]);
		msg->WriteDeltaFloat(oldState->materialParms[1], newState->materialParms[1]);
		msg->WriteDeltaFloat(oldState->materialParms[2], newState->materialParms[2]);
		msg->WriteDeltaFloat(oldState->materialParms[3], newState->materialParms[3]);
		msg->WriteDeltaFloat(oldState->materialParms[4], newState->materialParms[4]);
		msg->WriteDeltaFloat(oldState->materialParms[5], newState->materialParms[5]);
		msg->WriteDeltaFloat(oldState->materialParms[6], newState->materialParms[6]);
		msg->WriteDeltaFloat(oldState->materialParms[7], newState->materialParms[7]);
		msg->WriteDeltaLong(oldState->solidBox, newState->solidBox);
		msg->WriteDeltaLong(oldState->solidContents, newState->solidContents);
	}
}

/*
 ==================
 SG_WriteSnapshotEntities
 ==================
*/
static void SG_WriteSnapshotEntities (clientSnapshot_t *oldSnapshot, clientSnapshot_t *newSnapshot, ByteBuffer *msg){

	entityState_t	*oldState = NULL, *newState = NULL;
	int				oldIndex = 0, newIndex = 0;
	int				oldNum, newNum;
	int				oldNumEntities;

	// Generate the delta update
	if (!oldSnapshot)
		oldNumEntities = 0;
	else
		oldNumEntities = oldSnapshot->numEntities;

	while (newIndex < newSnapshot->numEntities || oldIndex < oldNumEntities){
		if (newIndex >= newSnapshot->numEntities)
			newNum = 9999;
		else {
			newState = &newSnapshot->entities[newIndex];
			newNum = newState->number;
		}

		if (oldIndex >= oldNumEntities)
			oldNum = 9999;
		else {
			oldState = &oldSnapshot->entities[oldIndex];
			oldNum = oldState->number;
		}

		if (newNum == oldNum){
			// Delta update from old position.
			// Because the force parameter is false, this will not result in
			// any bytes being emitted if the entity has not changed at all.
			SG_WriteDeltaEntityState(oldState, newState, false, msg);

			oldIndex++;
			newIndex++;

			continue;
		}

		if (newNum < oldNum){
			// This is a new entity, send it from the baseline
			SG_WriteDeltaEntityState(&level.entities[newNum]->baseline, newState, true, msg);

			newIndex++;

			continue;
		}

		if (newNum > oldNum){
			// The old entity isn't present in the new snapshot
			SG_WriteDeltaEntityState(oldState, NULL, true, msg);

			oldIndex++;

			continue;
		}
	}

	// End of snapshot entities
	msg->WriteBits(ENTITYNUM_NONE, ENTITYNUM_BITS);
}

/*
 ==================
 SG_WriteSnapshotToClient
 ==================
*/
void SG_WriteSnapshotToClient (client_t *client, ByteBuffer *msg){

	clientSnapshot_t	*oldSnapshot=0, *newSnapshot;
	byte				buffer[10];//BSP_MAX_PORTALS/8];
	int					bytes;
	int					i;

	// Free old snapshots that are no longer needed
	for (i = 0; i < PACKET_BACKUP; i++){
		if (!client->snapshots[i])
			continue;

		odMemory->Free(client->snapshots[i]->entities);
		odMemory->Free(client->snapshots[i]);

		client->snapshots[i] = NULL;
	}

	// Build the snapshot
	newSnapshot = SG_BuildClientSnapshot(client);

	// Write the snapshot header
	msg->WriteLong(level.time);
	msg->WriteShort(client->ping);
	msg->WriteBool(0);
	msg->WriteLong(0);

	// TODO: the portal bits buffer can be generated after each SG_RunFrame,
	// and used for all clients
	bytes = odCollision->WritePortalBits(buffer);

	// Write the portal bits
	msg->WriteByte(bytes);
	msg->WriteData(buffer, bytes);

	// Write the player state
	if (oldSnapshot)
		SG_WriteDeltaPlayerState(&oldSnapshot->state, &newSnapshot->state, msg);
	else
		SG_WriteDeltaPlayerState(NULL, &newSnapshot->state, msg);

	// Write the entities
	SG_WriteSnapshotEntities(oldSnapshot, newSnapshot, msg);
}
